// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Microsoft.WindowsMobile.Status;
using svs.Mobile.BeyondMedia.FeedCore.Helpers;
using svs.Mobile.BeyondMedia.FeedCore.RSS;

namespace svs.Mobile.BeyondMedia.FeedCore
{
	public static class CoreHelper
	{
		#region Delegates

		public delegate void FeedChangedEvent(Feed aTrack);

		public delegate void FeedSelectedEvent(Feed aFeed);

		public delegate void RepositoryLoadedEvent();

		public delegate void RepositorySaveEvent(bool isSaving);

		public delegate void TrackAddedEvent(Track i_track);

		public delegate void TrackBeforeDeletingEvent(Track i_track);

		public delegate void TrackChangedEvent(Track aTrack);

		public delegate void TrackDeletedEvent(Track i_track);

		#endregion

		#region PowerState enum

		public enum PowerState
		{
			On = 0x00010000,
			Off = 0x00020000,
			Unattended = 0x00400000,
			UserIdle = 0x01000000,
			Suspend = 0x00200000,
			BackLightOn = 0x02000000
		}

		#region Public Methods

		private const decimal GB = 1024*1024*1024;
		private const decimal MB = 1024*1024;

		public static string SYS_COLOR_A
		{
			get
			{
				return
					string.Format("#{0}{1}{2}", SystemColors.MenuText.R.ToString("X").PadLeft(2, '0'),
					              SystemColors.MenuText.G.ToString("X").PadLeft(2, '0'),
					              SystemColors.MenuText.B.ToString("X").PadLeft(2, '0'));
			}
		}

		public static Uri SafeCreateUri(string url)
		{
			if (string.IsNullOrEmpty(url))
				return null;

			string cleanUrl = url.Trim();

			try
			{
				return new Uri(cleanUrl, UriKind.Absolute);
			}
			catch(Exception ex)
			{
				CoreHelper.WriteLogEntry("Unable to create URL from: " + url +  "! Reason: " + ex.Message);
				return null;
			}
		}

		public static string GetFormattedDeviceConnections()
		{
			StringBuilder retVal = new StringBuilder();

			retVal.AppendFormat(CultureInfo.CurrentCulture,
			                    "<div style='color: {1}'><h3>Device Network Connections: <b>{0}</b></h3>",
			                    SystemState.ConnectionsCount, "DodgerBlue");

			retVal.Append("<ul style='margin-top:5px; margin-bottom:5px'>");

			if (SystemState.ConnectionsDesktopCount > 0)
				retVal.AppendFormat(CultureInfo.CurrentCulture,
				                    "<li>ActiveSync: <b>{0}</b> ({1})</li>",
				                    SystemState.ConnectionsDesktopCount, SystemState.ConnectionsDesktopDescriptions);

			if (SystemState.ConnectionsCellularCount > 0)
				retVal.AppendFormat(CultureInfo.CurrentCulture,
				                    "<li>Cellular (GPRS/EDGE/3G): <b>{0}</b> ({1})</li>",
				                    SystemState.ConnectionsCellularCount, SystemState.ConnectionsCellularDescriptions);

			if (SystemState.ConnectionsNetworkCount > 0)
				retVal.AppendFormat(CultureInfo.CurrentCulture,
				                    "<li>Network / WiFi: <b>{0}</b> ({1})</li>",
				                    SystemState.ConnectionsNetworkCount, SystemState.ConnectionsNetworkDescriptions);

			if (SystemState.ConnectionsBluetoothCount > 0)
				retVal.AppendFormat(CultureInfo.CurrentCulture,
				                    "<li>Bluetooth: <b>{0}</b> ({1})</li>",
				                    SystemState.ConnectionsBluetoothCount, SystemState.ConnectionsBluetoothDescriptions);

			if (SystemState.ConnectionsModemCount > 0)
				retVal.AppendFormat(CultureInfo.CurrentCulture,
				                    "<li>Modem: <b>{0}</b> ({1})</li>",
				                    SystemState.ConnectionsModemCount, SystemState.ConnectionsModemDescriptions);

			if (SystemState.ConnectionsProxyCount > 0)
				retVal.AppendFormat(CultureInfo.CurrentCulture,
				                    "<li>Proxy: <b>{0}</b> ({1})</li>",
				                    SystemState.ConnectionsProxyCount, SystemState.ConnectionsProxyDescriptions);

			if (SystemState.ConnectionsVpnCount > 0)
				retVal.AppendFormat(CultureInfo.CurrentCulture,
				                    "<li>VPN: <b>{0}</b> ({1})</li>",
				                    SystemState.ConnectionsVpnCount, SystemState.ConnectionsVpnDescriptions);

			if (SystemState.ConnectionsUnknownCount > 0)
				retVal.AppendFormat(CultureInfo.CurrentCulture,
				                    "<li>Unknown: <b>{0}</b> ({1})</li>",
				                    SystemState.ConnectionsUnknownCount, SystemState.ConnectionsUnknownDescriptions);

			retVal.Append("</ul></div>");

			return retVal.ToString();
		}

		/// <summary>
		/// Formats a datw with month/day or "today"
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		public static string FormatDate(DateTime? date)
		{
			if (!date.HasValue)
				return string.Empty;

			DateTime now = DateTime.Now;
			if (date.Value.Year == now.Year && date.Value.Month == now.Month && date.Value.Day == now.Day)
				return "Today";

			return ((DateTime) date).ToString("MMM d");
		}

		/// <summary>
		/// Formats a datw with month/day or "today" + time
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		public static string FormatDateTime(DateTime? date)
		{
			if (!date.HasValue)
				return string.Empty;

			return string.Format("{0} {1}", FormatDate(date), ((DateTime) date).ToString("h:mm tt"));
		}

		public static string GetFileLengthAsString(long? Length)
		{
			string retVal = string.Empty;

			if (Length.HasValue)
			{
				decimal size = Length.Value;

				if (size > GB)
				{
					retVal = string.Format("{0:F2} GB", Length/GB);
				}
				else if (size > MB)
				{
					retVal = string.Format("{0:F0} MB", Length/MB);
				}
				else if (size > 1024)
				{
					retVal = string.Format("{0:F0} KB", Length/1024);
				}
				else
				{
					retVal = string.Format("{0:F0} Bytes", Length);
				}
			}

			return retVal;
		}

		public static string GetManagedMemoryUsedAsString()
		{
			long mem = GC.GetTotalMemory(true);
			return mem/1024 + " K";
		}

		public static string GetTotalProcessMemoryUsedAsString()
		{
			if (ProcessHelper.MyProcess != null)
				return ProcessHelper.MyProcess.BaseAddess/1024 + " K";

			return string.Empty;
		}

		public static string GetTimeDurationAsString(int milliseconds)
		{
			TimeSpan ts = new TimeSpan(milliseconds*TimeSpan.TicksPerMillisecond);

			if (ts.TotalSeconds < 1)
				return ts.Milliseconds + " mSec.";

			if (ts.TotalMinutes < 1)
				return ts.Seconds + " sec.";
			
			if (ts.TotalHours < 1)
				return ts.Minutes + " min. " + ts.Seconds + " sec.";

			if (ts.Days < 1)
				return ts.Hours + "hr. " + ts.Minutes + " min. " + ts.Seconds + " sec.";

			return ts.ToString();
		}

		public static string GetDownloadSpeedAsString(long bytes, int milliseconds)
		{
			decimal sec = (decimal) milliseconds/1000;
			decimal kb = (decimal) bytes/1024;

			decimal retVal = 0;
			if (sec != 0)
			{
				retVal = kb/sec;
			}

			return String.Format("{0:F1} KB/Sec", retVal);
		}

		public static void WriteLogEntry(string i_entry)
		{
			StreamWriter sr = null;

			try
			{
				sr = File.AppendText(Configuration.LogFilePath);
				sr.WriteLine(string.Format("{0}: {1}", DateTime.Now, i_entry));
				sr.Close();
			}
			catch (Exception ex)
			{
				MessageBox.Show("Unable to write to the log file. Reason: " + ex);
			}
			finally
			{
				if (sr != null)
					sr.Close();
			}
		}

		public static void WriteTraceEntry(string i_Entry)
		{
			string lasttrace = string.Empty;
			if (m_LastTraceTime > 0)
			{
				lasttrace = GetTimeDurationAsString(Environment.TickCount - m_LastTraceTime);
			}

			m_LastTraceTime = Environment.TickCount;

			Debug.WriteLine(string.Format("{0}: {1} ({2})", DateTime.Now.ToShortTimeString(), i_Entry, lasttrace));

			if (Configuration.TraceLevel > 0)
			{
				m_TraceBuffer.AppendFormat(m_TraceCulture, "{0}: {1}\r\n", DateTime.Now, i_Entry);
			}
		}

		public static void SaveTraceBuffer()
		{
			if (Configuration.TraceLevel > 0)
			{
				WriteTraceBuffer();
			}
		}

		public static char GetAlternativeKey(char keyChar)
		{
			switch (keyChar)
			{
				case '1':
					return 'w';

				case '2':
					return 'e';

				case '3':
					return 'r';

				case '4':
					return 's';

				case '5':
					return 'd';

				case '6':
					return 'f';

				case '7':
					return 'x';

				case '8':
					return 'c';

				case '9':
					return 'v';

				default:
					return ' ';
			}
		}

		public static void DoPostInstalProcessing()
		{
			string copy = Path.Combine(Configuration.ApplicationInstallPath, "TrackState.xml.copy");
			string org = copy.Replace(".xml.copy", ".xml");

			if (File.Exists((org)) && File.Exists(copy))
				File.Delete(copy);
			else if (File.Exists(copy) && !File.Exists(org))
				File.Move(copy, org);
		}

		/// <summary>
		/// Makes sure that the string does not have any invalid path characters
		/// </summary>
		/// <param name="text"></param>
		/// <returns></returns>
		public static string MakeValidFileName(string text)
		{
			string retval = text.Replace("<", "");
			retval = retval.Replace(@">", "");
			retval = retval.Replace(@"\", "");
			retval = retval.Replace("/", "");
			retval = retval.Replace(":", "");
			retval = retval.Replace("*", "");
			retval = retval.Replace("?", "");
			retval = retval.Replace("\"", "");
			retval = retval.Replace("|", "");

			return retval;
		}

		public static string CreateFileNameFromUrl(string Uri, string mimetype, bool forceUnique, string prefix)
		{
			string stNameUrl = String.Empty; // url based name
			string stName = String.Empty;    // query parameter based name
			string stUrl = String.Empty;
			string stQuery = String.Empty;
			string stExtUrl = String.Empty;   // url based extension

  		// registry based extension (MIME type based)
			string stExtReg = (string)Microsoft.Win32.Registry.GetValue("HKEY_CLASSES_ROOT\\Mime\\Database\\Content Type\\" + mimetype, "Extension", null);  
			string stExt = ".mp3";
			string st;
			int isQuery = -1;

			try
			{
				// 1'st decode the transmission string
				stUrl = HttpUtility.UrlDecode(Uri);

				// 2'nd separate any query parameters 
				if ((isQuery = stUrl.IndexOf("?")) >= 0)
				{
					stQuery = stUrl.Substring(isQuery + 1);
					stUrl = stUrl.Substring(0, isQuery);
				}

				// 3'rd separate filename and get URL based extension
				stNameUrl = Path.GetFileName(stUrl);
				stExtUrl = Path.GetExtension(stNameUrl);
			}
			catch (Exception)
			{
				forceUnique = true;
			}

			// ???
			forceUnique |= string.Compare(stName, "redirect.mp3", true) == 0;

			// check if there are more than one parameters -> here we could fallback to stNameUrl also
			forceUnique |= stQuery.IndexOf("&") >= 0;

			// select the best extension
			stExt = stExtReg != null ? stExtReg :
							stExtUrl != String.Empty ? stExtUrl : ".mp3";

			if (forceUnique)
				return MakeValidFileName(prefix + Uri.GetHashCode() + stExt); // create a generic file name

			stName = stNameUrl;

			// if !forceUnique and we have a query parameters then we try to get the filename from the parameter
			if (stQuery != String.Empty)
			{
				// try to derive a good filename from query parameters
				int eq = stQuery.LastIndexOf("=");
				if (eq >= 0 && eq < stQuery.Length - 2)
					stName = stQuery.Substring(eq + 1); // assume the podcast is the last query string
				else if (eq < 0)
					stName = stQuery; // take the query parameter direct (missing '=')
			}

			// replace invalid chars
			stName = MakeValidFileName(stName);

			// if we don't have an extension (e.g. missing in the query parameter) we add the selected extension
			st = Path.GetExtension(stName);
			if (st == String.Empty || Microsoft.Win32.Registry.GetValue("HKEY_CLASSES_ROOT\\" + st, null, null) == null)
				stName = Path.ChangeExtension(stName, stExt);

			return stName;
		}

		/// <summary>
		/// Returns the duration of days between now and a date
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		public static double GetDaysOld(DateTime? date)
		{
			//Calculate the feed fresh-ness
			double daysold = 1000;

			if (date.HasValue)
			{
				DateTime cur = DateTime.Now;
				DateTime dt = (DateTime) (date);

				if (cur.Year == dt.Year && cur.Month == dt.Month && cur.Day == dt.Day)
					daysold = 0;
				else
					daysold = (DateTime.Now - dt).TotalDays;
			}

			return daysold;
		}

		public static void DumpUpdateStatistics()
		{
			StreamWriter sr = null;
			try
			{
				sr = File.CreateText(Configuration.FeedUpdateLogFilePath);

				sr.WriteLine(
					"<html><head><meta content='text/html; charset=utf-8' http-equiv='content-type'><style type='text/css'>.bpAction {color:Gray; font-size: smaller; text-decoration: none; padding: 0 2px;}</style></head><body bgcolor='AliceBlue'>");

				DateTime? updatedTime = RssFeedUpdateManager.Statistics.DownloadTime;

				if (!updatedTime.HasValue)
					updatedTime = EnclosureDownloadManager.Statistics.DownloadTime;

				sr.WriteLine(
					string.Format("<p class='bpAction' align=right'> Last Updated: {0}, took: {1} min.<p>",
					              updatedTime.HasValue
					              	? string.Format("{0} {1}", updatedTime.Value.ToString("MMM d"),
					              	                updatedTime.Value.ToString("h:mm tt"))
					              	: "Unknown!",
					              updatedTime.HasValue
					              	? Math.Round((DateTime.Now - ((DateTime) updatedTime)).TotalMinutes).ToString()
					              	: "0"));

				sr.WriteLine("<div style='margin-top:15px;color:" + SYS_COLOR_A + "; font-weight: bold;'>Updated Feeds</div>");
				sr.WriteLine("<hr style='color: " + SYS_COLOR_A + "; height: 0px;' /><ul>");

				RssFeedUpdateManager.Statistics.GetSuccesfulDownloads().ForEach(
					delegate(DownloadStatistics ds) { sr.WriteLine(string.Format("<li>{0}</li>", ds.OwnerFeed.Name)); });

				sr.WriteLine("</ul><div style='margin-top:15px;color:" + SYS_COLOR_A +
				             "; font-weight: bold;'>Downloaded Podcasts</div>");
				sr.WriteLine("<hr style='color: " + SYS_COLOR_A + "; height: 0px;' /><ul>");

				EnclosureDownloadManager.Statistics.GetSuccesfulDownloads().ForEach(
					delegate(DownloadStatistics ds)
						{
							sr.WriteLine(
								string.Format("<li>{0} ({1} in {2})</li>", ds.OwnerTrack.Name, GetFileLengthAsString(ds.DownloadedSize),
								              GetTimeDurationAsString(ds.DownloadTime + ds.TimeToFirstByte)));
						});

				sr.WriteLine("</ul><br/><br/>");

				sr.WriteLine(
					"<div align='right' style='text-align: right;margin-right: 20px;'><a class='bpAction' style='text-align: right;' href='#' onclick='detailsPnl.style.display= \"\"; '>More Details...</a></div>");

				sr.WriteLine("<div id='detailsPnl' style='margin-top: 5px; margin-left: 5px; margin-bottom: 5px;display:none;'>");

				sr.WriteLine("<h3> Feed Update Details</h3>");

				//Dump the last update statistics
				sr.WriteLine(RssFeedUpdateManager.Statistics);

				sr.WriteLine("<h3> Feed Processing Details</h3>");

				//Update and Download Statistics
				sr.WriteLine(UpdateAndDownloadManager.Statistics);

				sr.WriteLine("<h3>Feed Download Details</h3>");

				//Download Statistics
				sr.WriteLine(EnclosureDownloadManager.Statistics);

				//Device Connections
				sr.WriteLine(GetFormattedDeviceConnections());

				sr.WriteLine("</div></body>");

				RssFeedUpdateManager.Statistics.ClearStatistics();
				EnclosureDownloadManager.Statistics.ClearStatistics();
				UpdateAndDownloadManager.Statistics.ClearStatistics();
			}
			catch (Exception ex)
			{
				WriteLogEntry("Unable to save Update Statistics. Reason: " + ex.Message);
			}
			finally
			{
				if (sr != null)
					sr.Close();
			}
		}

		[DllImport("coredll.dll")]
		public static extern int GetAsyncKeyState(int vkey);

		/// <summary>
		/// Formats as time string.
		/// </summary>
		/// <param name="i_time">The i_time.</param>
		/// <returns></returns>
		public static string FormatAsTimeString(double i_time)
		{
			TimeSpan ts = new TimeSpan(0, 0, (int) i_time);

			if (ts.TotalHours > 1)
			{
				return String.Format("{0:D}:{1:D2}m", ts.Hours, ts.Minutes);
			}
			
			if (ts.TotalMinutes > 1)
			{
				return String.Format("{0:D}:{1:D2}s", ts.Minutes, ts.Seconds);
			}
			
			return String.Format("{0:D}s", ts.Seconds);
		}

		/// <summary>
		/// Updates file's las write timeto the current date time
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public static bool TouchFile(string file)
		{
			if (!File.Exists(file))
				return false;

			IntPtr hFile = IntPtr.Zero;

			try
			{
				hFile = CreateFile(file, 0x40000000 /*Write*/, (uint)(FileShare.Write), 0, 3 /*open exisitng*/, 0, 0);

				if ((int) hFile == -1)
						throw new Win32Exception(Marshal.GetLastWin32Error());

				bool res = SetFileTime(hFile, null, null, BitConverter.GetBytes(DateTime.Now.ToFileTime()));
				return res;
			}
			catch (Exception ex)
			{
				WriteLogEntry(string.Format("Unable to Touch file {0}! Reason: {1}", file, ex.Message));
				return false;
			}
			finally
			{
				CloseHandle(hFile);
			}
		}

		public static bool IsDeviceConnected()
		{
			return true;

			/*if(SystemState.ConnectionsCount > 0)
				return true;
			else
				{
					try
					{
						string hostName = Dns.GetHostName();
						IPHostEntry curHost = Dns.GetHostByName(hostName);
						return curHost.AddressList[0].ToString() != IPAddress.Loopback.ToString();
					}
					catch
					{
						return false;
					}
				}*/
		}

		#region PocketPC Power Management

		[DllImport("coredll.dll", SetLastError = true)]
		private static extern int SetSystemPowerState(string psState, int StateFlags, int Options);

		public static void SetDevicePowerState(PowerState state)
		{
			SetSystemPowerState(null, (int) state, 0);
		}

		public static void KeepDeviceAwake()
		{
			try
			{
				SystemIdleTimerReset();
			}
			catch 
			{
				//Do nothing - not supported on the desktop
			}
		}

		[DllImport("coredll.dll")]
		private static extern void SystemIdleTimerReset();

		#endregion

		#region Free Memory

		public static bool FreeProgramMemoryIfNeeded(int needed)
		{
			bool result = true;

			MEMORYSTATUS memStatus = new MEMORYSTATUS();
			GlobalMemoryStatus(ref memStatus);

			if (memStatus.dwAvailPhys < needed)
			{
				if (SHCloseApps(needed) != 0)
					result = false;
			}
			return result;
		}

		//Required P/Invoke declarations
		[DllImport("aygshell.dll")]
		public static extern int SHCloseApps(int dwMemSought);

		[DllImport("coredll.dll")]
		public static extern void GlobalMemoryStatus(ref MEMORYSTATUS lpBuffer);

		public struct MEMORYSTATUS
		{
			public int dwAvailPageFile;
			public int dwAvailPhys;
			public int dwAvailVirtual;
			public int dwLength;
			public int dwMemoryLoad;
			public int dwTotalPageFile;
			public int dwTotalPhys;
			public int dwTotalVirtual;
		}

		#endregion

		#endregion

		#region Private methods

		private static void WriteTraceBuffer()
		{
			StreamWriter sr = null;
			try
			{
				sr = File.AppendText(Configuration.TraceFilePath);
				sr.WriteLine("----------------- " + DateTime.Now + "---------------------");
				sr.Write(m_TraceBuffer.ToString());
			}
			finally
			{
				if (sr != null)
					sr.Close();
			}
		}

		#region FILE I/O Interop Methods

		[DllImport("coredll.dll", EntryPoint = "SetFileTime", SetLastError = true)]
		private static extern bool SetFileTime(IntPtr hFile, byte[] lpCreationTime, byte[] lpLastAccessTime,
		                                       byte[] lpLastWriteTime);
		/// <summary>
		/// CreateFile file access flags
		/// </summary>
		[Flags()]//, CLSCompliant(false)]
		internal enum FileAccess2 : uint
		{
			/// <summary>
			/// Read access to the file.  Data can be read from the file.
			/// </summary>
			Read = 0x80000000,
			/// <summary>
			/// Write access to the file.  Data can be written to the file.
			/// </summary>
			Write = 0x40000000,
			/// <summary>
			/// Execute permission. The file can be executed.
			/// </summary>
			Execute = 0x20000000,
			/// <summary>
			/// All permissions.
			/// </summary>
			All = 0xF0000000
		}

		/// <summary>
		/// CreateFile file flags
		/// </summary>
		public enum FileFlags : uint
		{
			/// <summary>
			/// Instructs the system to write through any intermediate cache and go directly to disk.
			/// The system can still cache write operations, but cannot lazily flush them.
			/// </summary>
			WriteThrough = 0x80000000,
			/// <summary>
			/// This flag is not supported; however, multiple read/write operations pending on a device at a time are allowed.
			/// </summary>
			Overlapped = 0x40000000,
			/// <summary>
			/// Indicates that the file is accessed randomly.
			/// The system can use this as a hint to optimize file caching.
			/// </summary>
			RandomAccess = 0x10000000,
			/// <summary>
			/// 
			/// </summary>
			SequentialScan = 0x08000000,
			/// <summary>
			/// 
			/// </summary>
			DeleteOnClose = 0x04000000
		}

		/// <summary>
		/// Specifies which action to take on files that exist, and which action to take when files do not exist.
		/// </summary>
		public enum FileCreateDisposition : int
		{
			/// <summary>
			/// Creates a new file.
			/// The function fails if the specified file already exists.
			/// </summary>
			CreateNew = 1,
			/// <summary>
			/// Creates a new file.
			/// If the file exists, the function overwrites the file and clears the existing attributes.
			/// </summary>
			CreateAlways = 2,
			/// <summary>
			/// Opens the file.
			/// The function fails if the file does not exist.
			/// </summary>
			OpenExisting = 3,
			/// <summary>
			/// Opens the file, if it exists.
			/// If the file does not exist, the function creates the file as if dwCreationDisposition were <b>CreateNew</b>.
			/// </summary>
			OpenAlways = 4,
			/// <summary>
			/// Opens the file.
			/// Once opened, the file is truncated so that its size is zero bytes. The calling process must open the file with at least Write access.
			/// </summary>
			TruncateExisting = 5,
			// <summary>
			// Not supported.
			// </summary>
			//OpenForLoader = 6,
		}


		/// <summary>
		/// Wrapper around the CreateFile API
		/// </summary>
		/// <param name="fileName">Path to the file or CE port name</param>
		/// <param name="desiredFileAccess">Specifies the type of access to the object. An application can obtain read access, write access, read-write (All) access.</param>
		/// <param name="shareMode">Specifies how the object can be shared.</param>
		/// <param name="creationDisposition">Specifies which action to take on files that exist, and which action to take when files do not exist.</param>
		/// <param name="flagsAndAttributes">Specifies the file attributes and flags for the file.</param>
		/// <returns>Handle to the created file</returns>
		public static IntPtr CreateFile(string fileName,
			FileAccess desiredFileAccess,
			FileShare shareMode,
			FileCreateDisposition creationDisposition,
			int flagsAndAttributes)
		{
			FileAccess2 desiredAccess;
			switch (desiredFileAccess)
			{
				case FileAccess.Read:
					desiredAccess = FileAccess2.Read;
					break;
				case FileAccess.ReadWrite:
					desiredAccess = FileAccess2.All;
					break;
				case FileAccess.Write:
					desiredAccess = FileAccess2.Write;
					break;
				default:
					desiredAccess = FileAccess2.Execute; //it will never get here and in fact Execute is never currently used. Left as a back door.
					break;
			}

			IntPtr hFile = IntPtr.Zero;

			hFile = CreateFile(fileName, (uint)desiredAccess, (uint)shareMode, 0, (uint)creationDisposition, (uint)flagsAndAttributes, 0);

			if ((int)hFile == -1)
			{
				throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to Create File");
			}

			return hFile;
		}

		[DllImport("coredll.dll", EntryPoint = "CreateFile", SetLastError = true)]
		private static extern IntPtr CreateFile(
			string lpFileName,
			uint dwDesiredAccess,
			uint dwShareMode,
			int lpSecurityAttributes,
			uint dwCreationDisposition,
			uint dwFlagsAndAttributes,
			int hTemplateFile);

		[DllImport("coredll.dll", SetLastError = true)]
		internal static extern bool CloseHandle(IntPtr hObject);

		#endregion

		#endregion

		#endregion

		private static readonly StringBuilder m_TraceBuffer = new StringBuilder();
		private static readonly CultureInfo m_TraceCulture = new CultureInfo("es-ES", true);
		private static int m_LastTraceTime = 0;

		#region Global Enums

		#region MediaEndAction enum

		public enum MediaEndAction
		{
			PlayNext = 0,
			DeleteAndPlayNext = 1,
			DoNothing = 2
		}

		#endregion

		#region PlayerView enum

		public enum PlayerView
		{
			Small,
			Normal,
			FullScreen
		}

		#endregion

		#region RadioMode enum

		public enum RadioMode
		{
			Off,
			On,
			Discoverable,
		}

		#endregion

		#region SortOrder enum

		public enum SortOrder
		{
			ByNameAsc,
			ByNameDesc,
			ByDateAsc,
			ByDateDesc,
			Unknown
		}

		#endregion

		#endregion

		#region Exceptions

		#region Nested type: EncounteredOpmlFeedException

		public class EncounteredOpmlFeedException : ApplicationException
		{
		}

		#endregion

		#region Nested type: IssuficientStorageSpaceException

		public class IssuficientStorageSpaceException : ApplicationException
		{
			public IssuficientStorageSpaceException(string message)
				: base(message)
			{
			}
		}

		#endregion

		public class UnableToEstablishNetworkConnectionException : ApplicationException
		{
			public UnableToEstablishNetworkConnectionException(string message)
				: base(message)
			{
			}
		}


		#endregion
	}
}